﻿//  Copyright © Decebal Mihailescu 2013-2014
//  All rights reserved. This source code and binaries are distributed under 
//  Microsoft Public PatternLicense (Ms-PL)
//  The latest code is available at http://VSDesignPatterns.codeplex.com

//  Redistributions of source code must retain the above copyright notice, 
//  this list of conditions and the following disclaimer. 
//  Redistributions in binary form must reproduce the above copyright notice, 
//  this list of conditions and the following disclaimer in the documentation 
//  and/or other materials provided with the distribution. 

//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER 
//  REMAINS UNCHANGED.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if VS2015
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
#endif
using Microsoft.VisualStudio.CommandBars;
using System.Resources;
using System.Reflection;
using System.Globalization;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio;
using System.ComponentModel;
using System.Collections;

namespace DesignPattterns
{
    using EnvDTE;
    using EnvDTE80;
    using DesignPatternsWizard;//required by extension methods
    using Common;

    static public class DTEUtils
    {    //see http://blogs.msdn.com/b/dr._ex/archive/2007/04/17/using-ivsproffercommands-to-retrieve-a-visual-studio-commandbar.aspx
        [ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface IOleServiceProvider
        {
            [PreserveSig]
            int QueryService([In]ref Guid guidService, [In]ref Guid riid,
               [MarshalAs(UnmanagedType.Interface)] out System.Object obj);
        }

        /// <summary>
        /// Get Selected text from the active window
        /// </summary>
        /// <param SearchProviderName="window"></param>
        /// <returns></returns>
        internal static string GetSelectedText(this Window window, out object context)
        {
            context = null;
            if (window == null)
                return null;

            if (window.Caption.Equals("Error List"))
            {
                context = WindowPopupEnum.ErrorList;
#if VS2008
                ErrorList errList = window.Object as ErrorList;
                if (errList != null)
                {
                    object[] objArray = errList.SelectedItems as object[];
                    if (objArray == null)
                        return null;
                    //  ErrorItem[] errItems = objArray as ErrorItem[];
                    //  if (errItems == null)
                    //  return null;
                    int num = 0;
                    if (num >= (int)objArray.Length)
                    {
                        return string.Empty;
                    }
                    // string description = ((ErrorItem)objArray[num]).Description;
                    ErrorItem errItem = objArray[num] as ErrorItem;
                    if (errItem == null)
                        return null;
                    return errItem.Description;
                }
                return null;

#else

                return (((dynamic)((System.MarshalByRefObject)(window.DTE.ActiveWindow.Object)))
                    .SelectedItems[0]).Description;
#endif
            }
            else if (window.Caption.Contains("Solution Explorer"))
            {
                context = WindowPopupEnum.ReferenceItem;
#if! VS2010
                UIHierarchy hierarchy = window.Object as UIHierarchy;
                if (hierarchy != null)
                {
                    Array selectedItems = hierarchy.SelectedItems as Array;
                    System.Collections.IEnumerator enumerator1 = selectedItems.GetEnumerator();
                    try
                    {
                        if (enumerator1.MoveNext())
                        {
                            UIHierarchyItem uiItem = enumerator1.Current as UIHierarchyItem;
                            if (uiItem != null)
                            {
                                string name = uiItem.Name;
                                return name;
                            }

                        }
                    }
                    finally
                    {
                        IDisposable disposable = enumerator1 as IDisposable;
                        if (disposable != null)
                        {
                            disposable.Dispose();
                        }
                    }
                }
                return null;
#else

                return (((dynamic)((System.MarshalByRefObject)(window.DTE.ActiveWindow.Object)))
                         .SelectedItems[0]).Name;//works only in VS2010
                //return (((dynamic)((Microsoft.VisualStudio.PlatformUI.UIHierarchyMarshaler)(_applicationObject.ActiveWindow.Object)))
                //         .SelectedItems[0]).Name;
#endif
            }
            else if (window.Kind == "Document")
            {
                string ext = Path.GetExtension(window.Caption).ToLower();
                switch (ext)
                {
                    case ".xaml":
                        context = WindowPopupEnum.XAMLEditor;
                        break;
                    default:// .cs, .vb etc
                        context = WindowPopupEnum.CodeWindow;
                        break;
                }


                Document activeDocument = window.DTE.ActiveDocument;
                if (activeDocument != null)
                {
                    TextSelection selection = activeDocument.Selection as TextSelection;
                    if (selection != null)
                    {
                        return selection.Text;

                    }
                }
                return string.Empty;
                // return ((TextSelection)_applicationObject.ActiveDocument.Selection).Text;
            }
            else
                return null;
            /*
           
           string str;
            string caption = window.Caption;
            string str1 = .(893);
            bool flag = caption.Equals(str1);
            if (!flag)
            {
                string caption1 = window.Caption;
                string str2 = .(2658);
                bool flag1 = caption1.Contains(str2);
                if (!flag1)
                {
                    Document activeDocument = this._applicationObject.ActiveDocument;
                    if (activeDocument != null)
                    {
                        Document document = this._applicationObject.ActiveDocument;
                        object selection = document.Selection;
                        if (selection != null)
                        {
                            Document activeDocument1 = this._applicationObject.ActiveDocument;
                            object obj = activeDocument1.Selection;
                            string text = ((TextSelection)obj).Text;
                            return text;
                        }
                    }
                    return string.Empty;
                }
              ////"Solution Explorer"
                Windows windows = this._applicationObject.Windows;
                string str3 = .(2693);
                Window window1 = windows.Item(str3);
                object obj1 = window1.Object;
                object selectedItems = ((UIHierarchy)obj1).SelectedItems;
                IEnumerator enumerator = ((Array)selectedItems).GetEnumerator();
                IEnumerator enumerator1 = enumerator;
                try
                {
                    bool flag2 = enumerator1.MoveNext();
                    if (flag2)
                    {
                        object current = enumerator1.Current;
                        string SearchProviderName = ((UIHierarchyItem)current).Name;
                        str = SearchProviderName;
                        return str;
                    }
                }
                finally
                {
                    IDisposable disposable = enumerator1 as IDisposable;
                    if (disposable != null)
                    {
                        disposable.Dispose();
                    }
                }
                return string.Empty;
            }
            else
            {
                Windows windows1 = this._applicationObject.Windows;
                string str4 = .(2581);
                Window window2 = windows1.Item(str4);
                object obj2 = window2.Object;
                object selectedItems1 = ((ErrorList)obj2).SelectedItems;
                object[] objArray = (object[])selectedItems1;
                int num = 0;
                if (num >= (int)objArray.Length)
                {
                    return string.Empty;
                }
                string description = ((ErrorItem)objArray[num]).Description;
                str = description;
            }
            return str; */
        }

        /// <summary>
        /// Get Selected text from the active window
        /// </summary>
        /// <param SearchProviderName="window"></param>
        /// <returns></returns>
        internal static SearchProvider.Context GetSelectedContext(this Window window, SearchProvider searchProvider)
        {


            if (window == null)
                return null;

            DTE2 dte = (window.DTE as DTE2);
            if (dte == null)
                return null;
            Project pr = dte.GetActiveSolutionProject();
            SearchProvider.Context context = searchProvider.CreateContext((pr == null) ? null : pr.GetProjectInfoDTE());

            if (pr != null)
                context.ProjectInfo = pr.GetProjectInfoDTE();
            if (window.Caption.Equals("Error List"))
            {
                // context.Suffix = "Error";
                //context.OriginalWnd = WindowPopupEnum.ErrorList;
#if VS2008
                ErrorList errList = window.Object as ErrorList;
                if (errList != null)
                {
                    object[] objArray = errList.SelectedItems as object[];
                    if (objArray == null)
                        return null;
                    //  ErrorItem[] errItems = objArray as ErrorItem[];
                    //  if (errItems == null)
                    //  return null;
                    int num = 0;
                    if (num >= (int)objArray.Length)
                    {
                        context.SelectedText = string.Empty;
                        return context;
                    }
                    // string description = ((ErrorItem)objArray[num]).Description;
                    ErrorItem errItem = objArray[num] as ErrorItem;
                    if (errItem == null)
                        return context;
                    context.SelectedText = errItem.Description;
                    return context;
                }
                return null;
#else
#if VS2015
                // var taskitems = VsShellUtilities.GetErrorListItems(context.ProjectInfo.GetAsService<Package>());
                //  var taskitems2 = VsShellUtilities.GetTaskItems3(context.ProjectInfo);
                dynamic selection = window.Selection;

                if (selection != null)
                {
                    dynamic errItems = selection.ErrorItems;//ErrorItemsWrapper
                    if (errItems == null)
                    {
                        return null;
                    }
                    else
                    {

                        context.SelectedText = string.Empty;
                        IEnumerable ienum = errItems as IEnumerable;
                        var enumerator = ienum.GetEnumerator();
                        while (enumerator.MoveNext())
                        {
                            var crtErrItem = enumerator.Current;
                            Type errItemType = crtErrItem.GetType();//Microsoft.VisualStudio.ErrorListPkg.Shims.ErrorItemWrapper
                            //get the Entry data member
                            FieldInfo entryFI = errItemType.GetField("Entry");
                            if (entryFI == null)
                                continue;
                            object entryVal = entryFI.GetValue(crtErrItem);
                            if (entryVal == null)
                                continue;
                            //get the Entry.IsSelected property
                            Type entryType = entryVal.GetType();//Microsoft.VisualStudio.Shell.TableControl.Implementation.SnapshotTableEntryViewModel
                            PropertyInfo entryPI = entryType.GetProperty("IsSelected",typeof(bool));
                            if (entryPI == null)
                                continue;
                            object isSel = entryPI.GetValue(entryVal);
                            if (isSel != null && (bool)isSel)
                            {// extract Description from errorItem
                                PropertyInfo errorItemPI = errItemType.GetProperty("Description", typeof(string));
                                if (errorItemPI == null)
                                    continue;
                                object errDescrObj = errorItemPI.GetValue(crtErrItem);
                                context.SelectedText = errDescrObj as string;//.ToString();
                                break;
                            }
                        }
                        return context;
                    }
                }
                return null;
#else//all but VS 2008 & VS2015
                context.SelectedText = (((dynamic)((System.MarshalByRefObject)(window.DTE.ActiveWindow.Object)))
                    .SelectedItems[0]).Description;
                return context;

#endif
#endif

            }
            else if (window.Caption.Contains("Solution Explorer"))
            {
                context.Suffix = "assembly";
                //context.OriginalWnd = WindowPopupEnum.ReferenceItem;
#if! VS2010
                UIHierarchy hierarchy = window.Object as UIHierarchy;
                if (hierarchy != null)
                {
                    Array selectedItems = hierarchy.SelectedItems as Array;
                    System.Collections.IEnumerator enumerator1 = selectedItems.GetEnumerator();
                    try
                    {
                        if (enumerator1.MoveNext())
                        {
                            UIHierarchyItem uiItem = enumerator1.Current as UIHierarchyItem;
                            if (uiItem != null)
                            {
                                context.SelectedText = uiItem.Name;
                                return context;
                            }

                        }
                    }
                    finally
                    {
                        IDisposable disposable = enumerator1 as IDisposable;
                        if (disposable != null)
                        {
                            disposable.Dispose();
                        }
                    }
                }
                return null;
#else

             context.SelectedText = (((dynamic)((System.MarshalByRefObject)(window.DTE.ActiveWindow.Object)))
                         .SelectedItems[0]).Name;//works only in VS2010
            return context;
#endif
            }
            else if (window.Kind == "Document")
            {
                context.Suffix = (window.Document == null) ? "" : window.Document.Language;
                //override suffix if possible
                string ext = Path.GetExtension(window.Caption).ToLower();
                var provider = MEFController.Instance.LoadedLangProvidersMetada.FirstOrDefault(md => md.Extensions.Contains(ext));
                if (provider != null)
                {
                    IEnumerable<CodeLangEnum> lngs = provider.SupportedLangs.GetSpecificAllFlags();
                    if (lngs.Any())
                    {
                        context.Suffix = lngs.First().GetDescription();
                        if (context.ProjectInfo != null && context.ProjectInfo.CodeLang == CodeLangEnum.ManagedCpp)
                            context.Suffix = CodeLangEnum.ManagedCpp.GetDescription();
                    }

                }
                /*
                switch (ext)
                {
                    case ".xaml":
                       // context.OriginalWnd = WindowPopupEnum.XAMLEditor;
                        context.Suffix = "xaml";
                        break;
                    case ".xml":
                        context.Suffix = "xml";
                        //context.OriginalWnd = WindowPopupEnum.CodeWindow;
                        break;
                    case ".html":
                    case ".htm":
                        context.Suffix = "html";
                        //context.OriginalWnd = WindowPopupEnum.CodeWindow;
                        break;
                    default:// .cs, .vb etc
                                var provider = MEFController.Instance.LoadedLangProvidersMetada.FirstOrDefault(md => md.Extensions.Contains(ext));
                                if (provider != null)
                                {
                                    IEnumerable<CodeLangEnum> lngs = provider.SupportedLangs.GetSpecificAllFlags();
                                    if (lngs.Any())
                                        context.Suffix = lngs.First().GetDescription();

                                }
                        break;
                }
                */

                Document activeDocument = window.DTE.ActiveDocument;
                if (activeDocument != null)
                {
                    TextSelection selection = activeDocument.Selection as TextSelection;
                    if (selection != null)
                    {
                        context.SelectedText = selection.Text;
                        return context;

                    }
                }
                context.SelectedText = string.Empty;
                return context;
            }
            else
                return context;
        }
        /*
        /// <summary>
        /// Search Menu clicks
        /// </summary>
        /// <param SearchProviderName="Ctrl"></param>
        /// <param SearchProviderName="CancelDefault"></param>
        internal static void oControl_Click(CommandBarButton ctrl, ref bool CancelDefault)
        {
            DTE2 dte = (ctrl.Application as DTE2);
            if (dte == null)
                return;
            object context = WindowPopupEnum.None;
            string selText = dte.ActiveWindow.GetSelectedText(out context);
            //String selText = GetSelectedText(_applicationObject.ActiveWindow);
            //string selText = _applicationObject.ActiveWindow.GetSelectedText();

            if (!string.IsNullOrEmpty(selText))
            {
                string encodedTxt = Uri.EscapeUriString(selText.Trim());

                // MessageBox.Show(this, encodedTxt ?? "NULL", ctrl.Caption);
                // System.Diagnostics.Process.Start(ctrl.Tag + encodedTxt);//starts external browse

                try
                {
                    switch (Utils.OnlineResourcesDestination)
                    {
                        case 1:
                            System.Diagnostics.Process proc = new System.Diagnostics.Process();
                            proc.StartInfo = new ProcessStartInfo(ctrl.Tag + encodedTxt);
                            proc.Start();
                            break;
                        case 2:
                            ItemOperations itemOperations = dte.ItemOperations;
                            if (itemOperations != null)
                            {
                                Window window = itemOperations.Navigate(ctrl.Tag + encodedTxt, vsNavigateOptions.vsNavigateOptionsDefault);
                            }
                            break;
                        default:
                            break;

                    }

                }
                catch
                {


                }
            }
        }*/
        internal static object FindCommandBar(this EnvDTE80.DTE2 applicationObject, Guid guidCmdGroup, uint menuID)
        {
            // Make sure the CommandBars collection is properly initialized, before we attempt to
            // use the IVsProfferCommands service.
            CommandBar menuBarCommandBar = ((CommandBars)applicationObject.CommandBars)["MenuBar"];

            // Retrieve IVsProfferComands via DTE's IOleServiceProvider interface
            IOleServiceProvider sp = (IOleServiceProvider)applicationObject;
            Guid guidSvc = typeof(IVsProfferCommands).GUID;
            Object objService;
            sp.QueryService(ref guidSvc, ref guidSvc, out objService);
            IVsProfferCommands vsProfferCmds = (IVsProfferCommands)objService;
            return vsProfferCmds.FindCommandBar(IntPtr.Zero, ref guidCmdGroup, menuID);
        }

        //see http://zakimirza.wordpress.com/2008/10/27/visual-studio-add-in-creating-oContext-menus/ for more nodes
        // or http://zakimirza.wordpress.com/visual-studio-2005-extensiblity-command-bars-and-their-controls/
        public const string ITEM = "Item";
        public const string WEB_ITEM = "Web Item";
        public const string MULTI_ITEM = "Cross Project Multi Item";
        public const string FOLDER = "Folder";
        public const string WEB_FOLDER = "Web Folder";
        public const string PROJECT = "Project";
        public const string PROJECT_NODE = "Project Node";
        public const string STANDARD = "Standard";
        public const string MENU_BAR = "MenuBar";

        //see http://stackoverflow.com/questions/3017063/visual-studio-2010-plug-in-adding-a-oContext-menu-to-the-solution-explorer/3436742#3436742
        public static CommandBar GetCommandBarByItemType(this CommandBars cmdbars, string itemType)
        {
            return cmdbars[itemType];
        }

        public static CommandBar GetCommandBarByItemType(this EnvDTE80.DTE2 applicationObject, string itemType)
        {
            return ((CommandBars)(applicationObject.CommandBars))[itemType];
        }

        public static CommandBarControl AddCommandBar(this Command command, string itemType, int pos, string toolTip)
        {
            CommandBar vsBarFolder = ((CommandBars)command.DTE.CommandBars)[itemType];
            if (vsBarFolder != null)
            {
                Logging.Instance.Log(3, "Making new command and standard tool bar");
                CommandBarControl ctrl = (CommandBarControl)command.AddControl(vsBarFolder, pos);
                ctrl.TooltipText = toolTip;
                return ctrl;
            }
            else
                return null;
        }
        //from http://www.c-sharpcorner.com/UploadFile/mgold/AddIns11292005015631AM/AddIns.aspx
        public static void CreateCopyrightInEveryFileInSolution(this DTE2 applicationObject)
        {
            try
            {
                // Create a copyright text for each code item in the project 
                // Get the array of projects from the active solution
                System.Array theProjects = (System.Array)applicationObject.ActiveSolutionProjects;
                EnvDTE.Project theProject = null;
                // make sure there is an active solution containing projects
                if (theProjects.Length > 0)
                {
                    // This add-in only works for the first project in the solution,
                    // This can be extended for multiple projects by putting a for loop here like
                    // foreach (Project theProject in theProjects)
                    theProject = (EnvDTE.Project)(theProjects.GetValue(0));
                    // Go through each project item in the project
                    foreach (ProjectItem p in theProject.ProjectItems)
                    {
                        Logging.Instance.Log(3, "ProjectItem = " + p.Name);
                        // Check to see if its a c# file
                        if (p.Name.Substring(p.Name.Length - 3, 3) == ".cs")
                        {
                            // Open the file as a source code file
                            EnvDTE.Window theWindow = p.Open(Constants.vsViewKindCode);
                            //Get a handle to the new document in the open window
                            TextDocument objTextDoc = (TextDocument)theWindow.Document.Object("TextDocument");
                            EditPoint objEditPoint = (EditPoint)objTextDoc.StartPoint.CreateEditPoint();
                            //Create an EditPoint and add some text.
                            objEditPoint.Insert("// This Code was created by Microgold Software Inc. for educational purposes" + '\n' + "// Copyright Microgold Software Inc. " + DateTime.Now.ToLongDateString() + "\n\n");
                            // Get the filename from the project item and save the file with the copyright information
                            string fName = p.get_FileNames(0);
                            p.SaveAs(fName);
                        }
                        // end if ".cs" file
                    }
                    // end for each project item
                }
                // end if any projects exists in projects collection
            }
            // end try block
            catch (Exception e)
            {
                Logging.Instance.Log(3, e.Message.ToString());
            }
        }


        public static void CreateNewFileWithCopyrightInfo(this DTE2 applicationObject)
        {
            //Create a new text document.
            Window wnd = applicationObject.ItemOperations.NewFile("General\\Text File", "", Constants.vsViewKindCode);

            //Get a handle to the new document.
            TextDocument objTextDoc = (TextDocument)applicationObject.ActiveDocument.Object("TextDocument");

            EditPoint objEditPoint = (EditPoint)objTextDoc.StartPoint.CreateEditPoint();
            //Create an EditPoint and add some text.
            objEditPoint.Insert("// This Code was created by Microgold Software Inc. for educational purposes" + '\n' + "// Copyright Microgold Software Inc. " + DateTime.Now.ToLongDateString());

        }

        //from http://stackoverflow.com/questions/3017063/visual-studio-2010-plug-in-adding-a-oContext-menu-to-the-solution-explorer
        public static Project GetSelectedProject(this DTE2 applicationObject)
        {
            try
            {

                if (applicationObject == null || applicationObject.Solution == null || applicationObject.Solution.Projects == null || applicationObject.Solution.Projects.Count < 1 || applicationObject.SelectedItems == null)
                    return null;
                var item = (applicationObject.SelectedItems.Count == 1) ? applicationObject.SelectedItems.Item(1) : null;
                if (item != null)
                {
                    if (item.Project != null)
                        return item.Project;
                    else
                        return (item.ProjectItem == null) ? null : item.ProjectItem.ContainingProject;
                }

            }
            catch (Exception ex)
            {
                Logging.Instance.Log(3, "Exception thrown in GetSelectedProject: {0}", ex.Message);

            }
            return null;
        }
        //http://msdn.microsoft.com/en-us/library/envdte._dte.activesolutionprojects.aspx
        public static Project GetActiveSolutionProject(this DTE2 applicationObject)
        {
            try
            {
                if (applicationObject.Solution == null || applicationObject.Solution.Projects == null || applicationObject.Solution.Projects.Count < 1)
                    return null;
                Array projectItems = (Array)applicationObject.ActiveSolutionProjects;
                if (projectItems.Length == 1 && projectItems.GetValue(0) != null)
                {
                    return ((Project)projectItems.GetValue(0));
                }
            }
            catch
            {
            }
            return null;
        }
        public static string GetActiveSolutionFileName(this DTE2 applicationObject)
        {
            try
            {
                if (applicationObject.Solution == null)
                    return null;
                return applicationObject.Solution.FileName;
            }
            catch
            {
            }
            return null;
        }
        //see http://stackoverflow.com/questions/1434720/get-a-list-of-solution-project-files-for-vs-add-in-or-dxcore-plugin
        public static Project GetProjectbyInfo(this DTE2 applicationObject, IProjectInfo info)
        {
            try
            {
                if (info == null || applicationObject == null || applicationObject.Solution == null
                    || applicationObject.Solution.Projects == null || applicationObject.Solution.Projects.Count < 1)
                    return null;
                Project pr = info.GetAsService<Project>();
                return pr;
                /*    var allProjects = GetAllSolutionProjects(applicationObject);
                    //{66A26720-8FB5-11D2-AA7E-00C04F688DDE}
                    return allProjects.SingleOrDefault(prj =>
                        {
                            try
                            {
                                return string.Compare(prj.FileName, info.FileName, true) == 0;
                            }
                            catch (Exception)
                            {//catch Not implemented exceptions and continue, shows in VS 2013

                                return false;
                            }
                        });
                    */

            }
            catch (Exception ex)
            {
                Logging.Instance.Log(3, "Exception in GetProjectbyInfo: {0}", ex.Message);
            }
            return null;
        }

        public static IEnumerable<Project> GetAllSolutionProjects(Solution sol)
        {
            var topProjects = sol.Projects.OfType<Project>();
            return topProjects.SelectMany(p =>
            {
                return p.FindProjects();
            });

        }
        public static bool IsSolutionLoaded(this DTE2 applicationObject)
        {
            if (applicationObject == null)
                return false;
            return applicationObject.Solution.IsOpen;

        }
        //see http://www.mztools.com/articles/2006/mz2006004.aspx
        public static ProjectItem FindProjectItemsByName(this DTE2 applicationObject, string fileName)
        {
            try
            {
                if (!applicationObject.Solution.IsOpen)
                {
                    MessageBox.Show("Please load or create a solution");
                }
                else
                {
                    return applicationObject.Solution.Projects.OfType<Project>().Select(p => p.FindProjectItemByName(fileName)).First(pi => pi != null);
                }
            }
            catch (System.Exception objException)
            {
                MessageBox.Show(objException.ToString());
            }
            return null;

        }
        public static void InsertText(this ProjectItem pi, string content)
        {
            if (pi == null || pi.Document == null)
                return;
            Document document = pi.Document;

            TextDocument editDoc = (TextDocument)document.Object("TextDocument");
            EditPoint objEditPt = editDoc.CreateEditPoint(null);
            objEditPt.StartOfDocument();
            document.ReadOnly = false;

            while (!objEditPt.AtEndOfDocument)
            {
                objEditPt.Delete(objEditPt.LineLength);
                objEditPt.LineDown(1);
            }

            objEditPt.DeleteWhitespace(vsWhitespaceOptions.vsWhitespaceOptionsHorizontal);
            objEditPt.DeleteWhitespace(vsWhitespaceOptions.vsWhitespaceOptionsVertical);
            objEditPt.Insert(content);
            Logging.Instance.Log(3, "saving file {0}", document.FullName);
            document.Save(document.FullName);
        }
        public static bool HasFile(this Project proj, string fileName)
        {
            if (proj == null || string.IsNullOrEmpty(fileName))
                return false;
            return DTEUtils.FindFile(Path.GetDirectoryName(proj.FullName), fileName);
        }
        public static bool DeleteProjectItem(this Project proj, string itemName)
        {
            if (proj == null || string.IsNullOrEmpty(itemName))
                return false;
            try
            {
                ProjectItem pi = proj.ProjectItems.FindProjectItemByName(itemName);

                if (pi.Document != null)
                {
                    if (pi.Document.ActiveWindow != null)
                        pi.Document.ActiveWindow.Close(vsSaveChanges.vsSaveChangesNo);
                }
                //the doc can be set to null by events fired
                if (pi.Document != null)
                    pi.Document.Close(vsSaveChanges.vsSaveChangesNo);

                pi.Remove();
                string fn = pi.get_FileNames(0);
                if (File.Exists(fn))
                    File.Delete(fn);
                return true;
            }
            catch (Exception)
            {

                return false;
            }
        }

        public static IProjectInfo GetProjectInfoDTE(this Project proj)
        {

            try
            {
                DesignPatterns.ProjectInfoDTE prjInf = new DesignPatterns.ProjectInfoDTE(proj);
                return prjInf;
            }
            catch
            {
                return null;
            }

        }

        public static IEnumerable<Property> GetNestedProperties(this Properties props)
        {
            return props.OfType<Property>().Where(p =>
            {
                try
                {
                    Properties prop = p.Value as Properties;
                    return prop != null;
                }
                catch (Exception)
                {
                    return false;
                }
            });
        }
        public static IEnumerable<Property> GetFlatProperties(this Properties props)
        {
            return props.OfType<Property>().Where(p =>
            {
                try
                {
                    Property prop = p.Value as Property;
                    return prop != null;
                }
                catch (Exception)
                {
                    return false;
                }
            });
        }
        // /*
        internal static CodeLangEnum GetProjectTypeGuids(this Project proj)
        {

            try
            {

                //reference Microsoft.VisualStudio.Shell.Interop 
                object service = GetService(proj.DTE, typeof(Microsoft.VisualStudio.Shell.Interop.IVsSolution).GUID);
                Microsoft.VisualStudio.Shell.Interop.IVsSolution solution = (Microsoft.VisualStudio.Shell.Interop.IVsSolution)service;

                Microsoft.VisualStudio.Shell.Interop.IVsHierarchy hierarchy;
                int result = solution.GetProjectOfUniqueName(proj.UniqueName, out hierarchy);

                if (result == 0)
                {

                    Microsoft.VisualStudio.Shell.Interop.IVsAggregatableProject aggregatableProject = hierarchy as Microsoft.VisualStudio.Shell.Interop.IVsAggregatableProject;
                    if (aggregatableProject == null)
                        return CodeLangEnum.None;
                    string projectTypeGuids;
                    result = aggregatableProject.GetAggregateProjectTypeGuids(out projectTypeGuids);
                    if (result != 0)
                        return CodeLangEnum.None;
                    else
                    {
                        string[] guids = projectTypeGuids.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                        foreach (string guid in guids)
                        {//complete list at http://www.codeproject.com/Reference/720512/List-of-Visual-Studio-Project-Type-GUIDs
                            switch (guid)
                            {
                                case "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}":
                                case "{593B0543-81F6-4436-BA1E-4747859CAAE2}":
                                case "{20D4826A-C6FA-45DB-90F4-C717570B9F32}":
                                case "{BF6F8E12-879D-49E7-ADF0-5503146B24B8}":
                                case "{EFBA0AD7-5A72-4C68-AF49-83D382785DCF}":
                                case "{6BC8ED88-2882-458C-8E55-DFD12B67127B}":
                                case "{F5B4F3BC-B597-4E2B-B552-EF5D8A32436F}":
                                case "{4D628B5B-2FBC-4AA6-8C16-197242AEB884}":
                                case "{C089C8C0-30E0-4E22-80C0-CE093F111A43}":
                                case "{14822709-B5A1-4724-98CA-57A101D1B079}":
                                    return CodeLangEnum.CSharp;

                                case "{CB4CE8C6-1BDB-4DC7-A4D3-65A1999772F8}":
                                case "{EC05E597-79D4-47f3-ADA0-324C4F7C7484}":
                                case "{68B1623D-7FB9-47D8-8664-7ECEA3297D4F}":
                                case "{F184B08F-C81C-45F6-A57F-5ABD9991F28F}":
                                case "{DB03555F-0C8B-43BE-9FF9-57896B3C5E56}":
                                case "{D59BE175-2ED0-4C54-BE3D-CDAA9F3214C8}":
                                    return CodeLangEnum.VB;

                                case "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}":
                                    if (string.Compare(proj.ProjectKeyword(), "ManagedCProj", true) == 0)
                                        return CodeLangEnum.ManagedCpp;
                                    else
                                        return CodeLangEnum.NativeCpp;

                                case "{F2A71F9B-5D33-465A-A702-920D77279786}":
                                    return CodeLangEnum.FSharp;

                                default:
                                    break;
                            }
                        }
                        return CodeLangEnum.None;
                    }
                }
                else
                    return CodeLangEnum.None;
            }
            catch (Exception ex)
            {

                Logging.Instance.Log(3, "Exceptiojn in  GetProjectTypeGuids: {0}", ex.Message);
            }

            return CodeLangEnum.None;
        }

        //internal static object GetService(object serviceProvider, System.Type type)
        //{
        //    return GetService(serviceProvider, type.GUID);
        //}

        internal static object GetService(object serviceProviderObject, System.Guid guid)
        {

            object service = null;
            //reference Microsoft.VisualStudio.OLE.Interop.dll
            Microsoft.VisualStudio.OLE.Interop.IServiceProvider serviceProvider = null;
            IntPtr serviceIntPtr;
            int hr = 0;
            Guid SIDGuid;
            Guid IIDGuid;

            SIDGuid = guid;
            IIDGuid = SIDGuid;
            serviceProvider = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)serviceProviderObject;
            hr = serviceProvider.QueryService(ref SIDGuid, ref IIDGuid, out serviceIntPtr);

            if (hr != 0)
            {
                System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(hr);
            }
            else if (!serviceIntPtr.Equals(IntPtr.Zero))
            {
                service = System.Runtime.InteropServices.Marshal.GetObjectForIUnknown(serviceIntPtr);
                System.Runtime.InteropServices.Marshal.Release(serviceIntPtr);
            }

            return service;
        }
        //    */
        public static string ProjectKeyword(this Project proj)
        {
            try
            {
                if (proj == null)
                    return null;
                Property p = proj.Properties.Item("keyword");
                if (p == null)
                    return null;
                return p.Value.ToString();

            }
            catch (Exception ex)
            {

                Logging.Instance.Log(3, "Unable to get ProjectKeyword because {0}", ex.Message);
                return null;
            }

        }
        private static bool IsManagedCpp(this Project proj)
        {
            try
            {
                Property p = proj.Properties.Item("keyword");
                if (p != null)
                    return string.Compare(p.Value.ToString(), "ManagedCProj", true) == 0;
                else
                    return false;//p.Value = "Win32Proj"
            }
            catch (Exception ex)
            {

                Logging.Instance.Log(3, "Unable to get ManagedCProj because {0}", ex.Message);
                return false;
            }

        }
        public static Dictionary<string, string> GetProjectProperties(this Project proj)
        {
#if DEBUG
            var properties = proj.Properties.OfType<Property>().OrderBy(q => q.Name);
#else
            var properties = proj.Properties.OfType<Property>();
#endif
            return properties.ToDictionary(p => p.Name, p =>
            {
                try
                {
                    return p.Value as string;
                }
                catch
                {
                    return null;
                }
            });
        }


        public static IWizardWindow CreateWizardWindowFromProject(this Project proj, IWin32Window vsWindow)
        {
            try
            {
                if (proj == null)
                    return null;

                if (proj.GuessProjectType() == CodeLangEnum.None)
                {
                    MessageBox.Show(vsWindow, string.Format("the project's {0} '{1}' language is not supported", proj.Name, (proj.CodeModel == null) ? "Undefined" : proj.CodeModel.Language));
                    return null;
                }
                //vsWindow is a ref to Package/addin
                IProjectInfo prjInf = proj.GetProjectInfoDTE().GetAsService<IProjectInfo>();
                WizardForm frm = new WizardForm(prjInf);
                frm.VSWindow = vsWindow;
                return frm;
            }
            catch
            {

                return null;
            }
        }

        public static ProjectItem FindProjectItemByName(this Project objProject, string fileName)
        {

            string sMsg = null;
            ProjectItem projectItem = default(ProjectItem);

            sMsg = "Project Name: " + objProject.Name;
            sMsg += Environment.NewLine + "Kind: " + GuessProjectType(objProject);//DecodeProjectKind(objProject.Kind);

            try
            {
                projectItem = objProject.ParentProjectItem;
            }
            catch
            {
            }

            if ((projectItem != null))
            {
                sMsg += Environment.NewLine + "Parent Project Item: " + projectItem.Name;
            }
            else
            {
                sMsg += Environment.NewLine + "Parent Project Item: None";
                Logging.Instance.Log(2, "In DTEUtils.FindProjecItemByName " + sMsg);
            }

            // Logging.Instance.Log(3,sMsg); too much logging

            return FindProjectItemByName(objProject.ProjectItems, fileName);

        }


        public static ProjectItem FindProjectItemByName(this ProjectItems colProjectItems, string fileName)
        {
            string sMsg = null;

            if ((colProjectItems != null))
            {

                foreach (ProjectItem objProjectItem in colProjectItems)
                {

                    sMsg = "Project Item Name: " + objProjectItem.Name;
                    sMsg += Environment.NewLine + "Kind: " + DecodeProjectItemKind(objProjectItem.Kind);
                    sMsg += Environment.NewLine + "Containing Project: " + objProjectItem.ContainingProject.Name;
                    if (objProjectItem.Collection.Parent is Project)
                    {
                        sMsg += Environment.NewLine + "Parent (project): " + ((Project)objProjectItem.Collection.Parent).Name;
                    }
                    else if (objProjectItem.Collection.Parent is ProjectItem)
                    {
                        sMsg += Environment.NewLine + "Parent (project item): " + ((ProjectItem)objProjectItem.Collection.Parent).Name;
                    }
                    //Logging.Instance.Log(3,sMsg); too much logging
                    if (fileName == objProjectItem.Name)
                        return objProjectItem;
                    if ((objProjectItem.SubProject != null))
                    {
                        // We navigate recursively because it can be:
                        // - An Enterprise project in Visual Studio .NET 2002/2003
                        // - A solution folder in VS 2005
                        ProjectItem pi = FindProjectItemByName(objProjectItem.SubProject, fileName);
                        if (pi != null)
                            return pi;
                    }
                    else
                    {
                        // We navigate recursively because it can be:
                        // - An folder inside a project
                        // - A project item with nested project items (code-behind files, etc.)
                        ProjectItem pi = FindProjectItemByName(objProjectItem.ProjectItems, fileName);
                        if (pi != null)
                            return pi;

                    }

                }

            }
            return null;
        }

        public static IList<Project> FindProjects(this Project proj)
        {
            List<Project> projects = new List<Project>();
            if ((proj.ConfigurationManager != null))//{66A26720-8FB5-11D2-AA7E-00C04F688DDE} for folder
            {
                // return new List<Project>() { proj };
                projects.Add(proj);
                // It's a project!
                //outputPathProperty = proj.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath");
                //if (outputPathProperty == null)
                //{
                //    outputPath = "<no output path set>";
                //}
                //else
                //{
                //    outputPath = outputPathProperty.Value;
                //}
            }
            else
            {
                NavProjItems(proj.ProjectItems, projects);
            }
            return projects;

        }

        private static void NavProjItems(ProjectItems projItems, List<Project> projects)
        {
            // EnvDTE.ProjectItem projectItem = default(EnvDTE.ProjectItem);

            if ((projItems != null))
            {
                foreach (EnvDTE.ProjectItem projectItem in projItems)
                {
                    if ((projectItem.SubProject != null))
                    {
                        // Recurse, can be an Enterprise project in 
                        // Visual Studio .NET 2002/2003 or a solution folder in VS 2005
                        projects.AddRange(FindProjects(projectItem.SubProject));
                    }
                }
            }
        }
        public static CodeLangEnum GuessProjectType(this Project project)
        {
            CodeLangEnum res = CodeLangEnum.None;
            try
            {

                if (project == null || project.CodeModel == null || project.CodeModel.Language == null || project.FileName == null)
                    return CodeLangEnum.None;

                //F# {f2a71f9b-5d33-465a-a702-920d77279786}
                if (project != null && Path.GetExtension(project.FileName).ToUpperInvariant() == ".FSPROJ")
                    return CodeLangEnum.FSharp;
                switch (project.CodeModel.Language)
                {

                    case EnvDTE.CodeModelLanguageConstants.vsCMLanguageVB:
                        res = CodeLangEnum.VB;
                        break;
                    case EnvDTE.CodeModelLanguageConstants.vsCMLanguageCSharp:
                        res = CodeLangEnum.CSharp;//TypeScript projects are seen as C# projects too
                        break;
                    //http://msdn.microsoft.com/en-us/library/ms228770(v=vs.80).aspx
                    case EnvDTE.CodeModelLanguageConstants.vsCMLanguageVC:
                        if (string.Compare(project.ProjectKeyword(), "ManagedCProj", true) == 0)
                            res = CodeLangEnum.ManagedCpp;
                        else
                            res = CodeLangEnum.NativeCpp;
                        break;

                    case EnvDTE.CodeModelLanguageConstants.vsCMLanguageMC:
                        res = CodeLangEnum.ManagedCpp;
                        break;
                    default:
                        ////more on it at http://www.codeproject.com/Reference/720512/List-of-Visual-Studio-Project-Type-GUIDs
                        res = project.GetProjectTypeGuids();
                        break;

                }

            }
            catch (Exception objException)
            {
#if DEBUG
                //System.Diagnostics.Debugger.Break();
#endif
                Logging.Instance.Log(1, "Can't guess project/file VSWndType, exception{0}", objException);
                res = CodeLangEnum.None;
            }
            return res;

        }
        internal static bool FindFile(string startFolder, string filename)
        {

            // Take a snapshot of the file system.
            System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(startFolder);

            // This method assumes that the application has discovery permissions 
            // for all folders under the specified prex.
            IEnumerable<System.IO.FileInfo> fileList = dir.GetFiles("*.*", System.IO.SearchOption.AllDirectories);
            return fileList.Any(fi => string.Compare(fi.Name, filename, true) == 0);

        }
        public static string DecodeProjectItemKind(string sProjectItemKind)
        {

            string sResult = null;

            switch (sProjectItemKind)
            {
                case EnvDTE.Constants.vsProjectItemKindMisc:
                    sResult = "EnvDTE.Constants.vsProjectItemKindMisc";
                    break;
                case EnvDTE.Constants.vsProjectItemKindPhysicalFile:
                    sResult = "EnvDTE.Constants.vsProjectItemKindPhysicalFile";
                    break;
                case EnvDTE.Constants.vsProjectItemKindPhysicalFolder:
                    sResult = "EnvDTE.Constants.vsProjectItemKindPhysicalFolder";
                    break;
                case EnvDTE.Constants.vsProjectItemKindSolutionItems:
                    sResult = "EnvDTE.Constants.vsProjectItemKindSolutionItems";
                    break;
                case EnvDTE.Constants.vsProjectItemKindSubProject:
                    sResult = "EnvDTE.Constants.vsProjectItemKindSubProject";
                    break;
                case EnvDTE.Constants.vsProjectItemKindVirtualFolder:
                    sResult = "EnvDTE.Constants.vsProjectItemKindVirtualFolder";
                    break;
                default:
                    sResult = "";
                    break;
            }

            return sResult;

        }
    }
}
